package light.network;

import java.nio.Buffer;
import java.nio.ByteBuffer;

/**
 * 数据包
 * 
 * @author tanjiazhang 创建方式 Pack.allocate(capacity)
 */
public class UdpPacket implements Packet {

	private static final int MaxLength = 32 * 1024;
	private static final int Offset = 9;
	private final static int ACT = 31341369;

	static Integer idSequence = 0;

	static int genId() {
		synchronized (idSequence) {
			idSequence++;
			return idSequence;
		}
	}

	public static UdpPacket newActPack(int packId) {
		UdpPacket p = new UdpPacket(8, 0, false, false);
		p.putIsActPacket();
		p.putActPacketId(packId);
		p.data.position(0);
		p.data.limit(8);
		// p.data =
		// (ByteBuffer)ByteBuffer.allocate(9).putInt(ACT).putInt(packId).flip();
		return p;
	}

	ByteBuffer data;

	public static UdpPacket allocate(int capacity) {
		return new UdpPacket(Offset, capacity, true);
	}

	public static UdpPacket allocate(int capacity, boolean needAct) {
		return new UdpPacket(Offset, capacity, true, needAct);
	}

	public static UdpPacket wrap(ByteBuffer src) {
		return new UdpPacket(src, false);
	}

	public static UdpPacket create(ByteBuffer src) {
		return new UdpPacket(src, true);
	}

	private UdpPacket(int offset, int capacity, boolean needAct) {
		this(offset, capacity, true, needAct);
	}

	private UdpPacket(int offset, int capacity, boolean hasActFlag, boolean needAct) {
		data = ByteBuffer.allocate(capacity + offset);
		data.position(offset);
		putId();
		if (hasActFlag) {
			if (needAct)
				putNeedActFlag();
			else
				putNotneedActFlag();
		}
	}

	private UdpPacket(ByteBuffer src, boolean create) {
		data = src;
		if (create) {
			data.position(Offset);
			putPackLength(data.limit() - Offset);
			putId();
		}
	}

	public int getId() {
		return data.getInt(4);
	}

	@Override
	public int getLength() {
		return data.getInt(0);
	}

	public boolean isActPacket() {
		return data.getInt(0) == ACT;
	}

	private void putId() {
		data.putInt(4, genId());
	}

	private void putPackLength(int len) {
		data.putInt(0, len);
	}

	private void putIsActPacket() {
		data.putInt(0, ACT);
	}

	private void putActPacketId(int packId) {
		data.putInt(4, packId);
	}

	private void putNeedActFlag() {
		data.put(8, (byte) 1);
	}

	private void putNotneedActFlag() {
		data.put(8, (byte) 0);
	}

	public boolean isNeedAct() {
		return data.get(8) == 1;
	}

	@Override
	public boolean validate() {
		int len = getLength();
		if (len > MaxLength)
			return false;
		if (len == (data.limit() - Offset))
			return true;
		return false;
	}

	@Override
	public ByteBuffer buffer() {
		return data;
	}

	public static void main(String[] args) {
		UdpPacket ac = UdpPacket.newActPack(1);
		System.out.println(ac.buffer().getInt(0));
		System.out.println(ac.isActPacket());
		System.out.println("sendact " + ac.buffer().limit() + ","
				+ ac.buffer().position());

		ByteBuffer t = ByteBuffer.allocate(10);
		t.putInt(0, 18);
		t.flip();

		Buffer b = ByteBuffer.allocate(8).putInt(ACT).putInt(18).flip().flip();
		System.out.println(b.limit());
	}

	@Override
	public final byte[] array() {
		return data.array();
	}

	@Override
	public final int capacity() {
		return data.capacity() - Offset;
	}

	@Override
	public final Packet clear() {
		limit(capacity());
		position(0);
		return this;
	}

	@Override
	public final Packet flip() {
		if (data.position() > 0) {
			data.limit(position() );
			putPackLength(position()-Offset);
			data.position(0);
		}
		return this;
	}

	//
	// /* (non-Javadoc)
	// * @see light.network.Packet#flip()
	// */
	// @Override
	// public final Packet flip()
	// {
	// if(data.position()>0)
	// data.flip();
	// return this;
	// }

	/*
	 * (non-Javadoc)
	 * 
	 * @see light.network.Packet#resetRead()
	 */
	// @Override
	// public Packet resetRead()
	// {
	// data.position(0);
	// return this;
	// }

	/*
	 * (non-Javadoc)
	 * 
	 * @see light.network.Packet#get()
	 */
	@Override
	public byte get() {
		return data.get();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see light.network.Packet#get(int)
	 */
	@Override
	public byte get(int index) {
		return data.get(index + Offset);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see light.network.Packet#getChar()
	 */
	@Override
	public char getChar() {
		return data.getChar();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see light.network.Packet#getChar(int)
	 */
	@Override
	public char getChar(int index) {
		return data.getChar(index + Offset);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see light.network.Packet#getDouble()
	 */
	@Override
	public double getDouble() {
		return data.getDouble();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see light.network.Packet#getDouble(int)
	 */
	@Override
	public double getDouble(int index) {
		return data.getDouble(index + Offset);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see light.network.Packet#getFloat()
	 */
	@Override
	public float getFloat() {
		return data.getFloat();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see light.network.Packet#getFloat(int)
	 */
	@Override
	public float getFloat(int index) {
		return data.getFloat(index + Offset);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see light.network.Packet#getInt()
	 */
	@Override
	public int getInt() {
		return data.getInt();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see light.network.Packet#getInt(int)
	 */
	@Override
	public int getInt(int index) {
		return data.getInt(index + Offset);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see light.network.Packet#getLong()
	 */
	@Override
	public long getLong() {
		return data.getLong();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see light.network.Packet#getLong(int)
	 */
	@Override
	public long getLong(int index) {
		return data.getLong(index + Offset);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see light.network.Packet#getShort()
	 */
	@Override
	public short getShort() {
		return data.getShort();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see light.network.Packet#getShort(int)
	 */
	@Override
	public short getShort(int index) {
		return data.getShort(index + Offset);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see light.network.Packet#limit()
	 */
	@Override
	public final int limit() {
		return data.limit() - Offset;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see light.network.Packet#limit(int)
	 */
	@Override
	public final Packet limit(int newLimit) {
		data.limit(newLimit + Offset);
		return this;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see light.network.Packet#position()
	 */
	@Override
	public final int position() {
		return data.position();// - Offset;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see light.network.Packet#position(int)
	 */
	@Override
	public final Packet position(int newPosition) {
		data.position(newPosition + Offset);
		return this;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see light.network.Packet#put(byte)
	 */
	@Override
	public Packet put(byte b) {
		data.put(b);
		return this;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see light.network.Packet#put(byte[], int, int)
	 */
	@Override
	public Packet put(byte[] src, int offset, int length) {
		data.put(src, offset, length);
		return this;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see light.network.Packet#put(byte[])
	 */
	@Override
	public final Packet put(byte[] src) {
		data.put(src);
		return this;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see light.network.Packet#put(java.nio.Packet)
	 */
	@Override
	public Packet put(ByteBuffer src) {
		data.put(src);
		return this;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see light.network.Packet#put(int, byte)
	 */
	@Override
	public Packet put(int index, byte b) {
		data.put(index + Offset, b);
		return this;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see light.network.Packet#putChar(char)
	 */
	@Override
	public Packet putChar(char value) {
		data.putChar(value);
		return this;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see light.network.Packet#putChar(int, char)
	 */
	@Override
	public Packet putChar(int index, char value) {
		data.putChar(index + Offset, value);
		return this;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see light.network.Packet#putDouble(double)
	 */
	@Override
	public Packet putDouble(double value) {
		data.putDouble(value);
		return this;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see light.network.Packet#putDouble(int, double)
	 */
	@Override
	public Packet putDouble(int index, double value) {
		data.putDouble(index + Offset, value);
		return this;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see light.network.Packet#putFloat(float)
	 */
	@Override
	public Packet putFloat(float value) {
		data.putFloat(value);
		return this;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see light.network.Packet#putFloat(int, float)
	 */
	@Override
	public Packet putFloat(int index, float value) {
		data.putFloat(index + Offset, value);
		return this;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see light.network.Packet#putInt(int, int)
	 */
	@Override
	public Packet putInt(int index, int value) {
		data.putInt(index + Offset, value);
		return this;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see light.network.Packet#putInt(int)
	 */
	@Override
	public Packet putInt(int value) {
		data.putInt(value);
		return this;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see light.network.Packet#putLong(int, long)
	 */
	@Override
	public Packet putLong(int index, long value) {
		data.putLong(index + Offset, value);
		return this;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see light.network.Packet#putLong(long)
	 */
	@Override
	public Packet putLong(long value) {
		data.putLong(value);
		return this;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see light.network.Packet#putShort(int, short)
	 */
	@Override
	public Packet putShort(int index, short value) {
		data.putShort(index + Offset, value);
		return this;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see light.network.Packet#putShort(short)
	 */
	@Override
	public Packet putShort(short value) {
		data.putShort(value);
		return this;
	}

//	@Override
//	public String toString(String encoder) throws UnsupportedEncodingException {
//		return new String(data.array(), data.position() + Offset, data.limit()
//				- data.position(), encoder);
//	}

	@Override
	public ByteData data() {
		return new ByteData(data.array(), Offset, getLength());
	}

}